package hirt;

import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.nio.ByteBuffer;
import java.util.Vector;

/**
 * Item2D is derived from the Item class. As its name suggests, it is two-
 * dimensional, providing a position, width, height, and even angle.
 * @author puzzud
 */
public class Item2D extends Item
{
    /**
     * Two-dimensional position of this item.
     */
    Point position;

    /**
     * dimensions of this item.
     */
    int width, height;

    /**
     * Rotational angle of this item (stored in radians).
     */
    float angle;

    /**
     * States specific to an Item2D.
     */
    public final static int STATE_PLACING   = 0;
    public final static int STATE_PLACED    = 1;
    public final static int STATE_OFFSCREEN = 2;

    /**
     * Default constructor (sets most properties to -1 or 0).
     */
    public Item2D()
    {
        super();

        position = new Point( 0, 0 );
        width = -1;
        height = -1;
        angle = 0.0f;
        type = "";
    }

    /**
     * Constructor that specifies a sub type and an owner.
     * @param type sub type of this item2d.
     * @param owner reference to the owner for this new item2d.
     */
    public Item2D(String type, Item owner)
    {
        super(type,owner);
        position = new Point( 0, 0 );
        width = -1;
        height = -1;
        angle = 0.0f;
    }

    /**
     * Constructor that specifies sub type and an owner, but also positions
     * it.
     * @param type sub type of this item2d.
     * @param owner reference to the owner for this new item2d.
     * @param x horizontal coordinate of this item2d.
     * @param y vertical coordinate of this item2d.
     */
    public Item2D(String type, Item owner, int x, int y)
    {
        super(type,owner);
        position = new Point( x, y );
        width = -1;
        height = -1;
        angle = 0.0f;
    }

    /**
     * Builds an Item from a save string, presumably created by an item when saving to a file
     * this item should be identical to that one
     * @param saveString
     * @param items: Master Item List used to build the Item
     */
    public Item2D(String text, Vector<Item> items)
    {
        fromSaveString(text,items);
    }

    /**
     * Builds an Item from a byte stream, presumably created by an item across the network
     * this item should be identical to that one
     * @param bytes
     * @param items: Master Item List used to build the Item
     */
    public Item2D(ByteBuffer bytes, Vector<Item> items)
    {
        fromBytes(bytes,items);
    }

    /**
     * Builds an Item from a byte stream, presumably created by an item across the network
     * this item should be identical to that one
     * @param bytes
     * @param items: Master Item List used to build the action
     * @return what's left of the parem bytes after this method is completes
     */
    @Override
    protected ByteBuffer fromBytes(ByteBuffer bytes, Vector<Item> items)
    {
        bytes = super.fromBytes(bytes, items);
        position = ByteChanger.getPoint(bytes, 0);
        width = bytes.getInt(8);
        height = bytes.getInt(12);
        angle = bytes.getFloat(16);
        try {
            bytes = ByteChanger.truncate(bytes, 20);
        } catch (Exception ex) {

        }
        return bytes;
    }

    public Point getPosition()
    {
        return( position );
    }

    public int getXPosition()
    {
        return( position.x );
    }

    public int getYPosition()
    {
        return( position.y );
    }

    public void setPosition( Point position )
    {
        this.position = position;
    }

    public void setPosition( int xPosition, int yPosition )
    {
        position.x = xPosition;
        position.y = yPosition;
    }

    public void setXPosition( int xPosition )
    {
        position.x = xPosition;
    }

    public void setYPosition( int yPosition )
    {
        position.y = yPosition;
    }

    public int getWidth()
    {
        return( width );
    }

    public void setWidth( int width )
    {
        this.width = width;
    }

    public int getHeight()
    {
        return( height );
    }

    public void setHeight( int height )
    {
        this.height = height;
    }

    /**
     * Return this item2d's angle in degrees (as oppose to the stored radian form).
     * @return current angle in degrees.
     */
    public float getAngle()
    {
        return ( ( float )Math.toDegrees( angle ) );
    }

    /**
     * Set the angle in degrees.
     * @param angle new angle in degrees.
     */
    public void setAngle( float angle )
    {
        this.angle = ( float )Math.toRadians( angle );
    }

    /**
     * Perform collision detection on another item2d. This function returns
     * the truth of whether these two items collide or not.
     * @param item other item2d to detect collision with.
     * @return whether item2d's collide.
     */
    public boolean detectCollision( Item2D item )
    {
        return( detectCollision( this , item ) );
    }

    /**
     * Perform collision detection on two item2d's. This function returns the
     * truth of whether these two items collide or not. It is noteworthy to
     * mention that this function compares rotated items by polygonal bounding
     * boxes. It does not perform pixel by pixel collision.
     * @param item1 first item to compare.
     * @param item2 second item to compare.
     * @return whether item2d's collide.
     */
    public static boolean detectCollision( Item2D item1, Item2D item2 )
    {
        // TODO: this will need to get complicated when items are being rotated.
        if( ( item1 == null ) || ( item2 == null ) )
        {
            return( false );
        }

        // Check to see if these items are rotated or not.
        if( ( item1.angle != 0.0f ) || item2.angle != 0.0f )
        {
            // If even just one is, we need to do polygon.
            Polygon p1 = new Polygon();
            Polygon p2 = new Polygon();
            
            int item1XPosition = item1.getXPosition();
            int item1YPosition = item1.getYPosition();
            int item1Width = item1.getWidth();
            int item1Height = item1.getHeight();
            
            int item2XPosition = item2.getXPosition();
            int item2YPosition = item2.getYPosition();
            int item2Width = item2.getWidth();
            int item2Height = item2.getHeight();
            
            p1.addPoint( 0, 0 );
            p1.addPoint( 0 + item1Width, 0 );
            p1.addPoint( 0 + item1Width, 0 + item1Height );
            p1.addPoint( 0, 0 + item1Height );
            
            p2.addPoint( 0, 0 );
            p2.addPoint( 0 + item2Width, 0 );
            p2.addPoint( 0 + item2Width, 0 + item2Height );
            p2.addPoint( 0, 0 + item2Height );

            Area a1 = new Area( p1 );
            Area a2 = new Area( p2 );

            // TODO: rotations now.
            AffineTransform at;

            at = new AffineTransform();
            at.setToIdentity();

            at.translate( item1XPosition, item1YPosition );
            at.rotate( ( float )Math.toRadians( item1.getAngle() ), item1Width/2, item1Height/2 );
            a1.transform( at );

            //at = new AffineTransform();
            at.setToIdentity();

            at.translate( item2XPosition, item2YPosition );
            at.rotate( ( float )Math.toRadians( item2.getAngle() ), item2Width/2, item2Height/2 );
            a2.transform( at );

            a1.intersect(a2);
            return( !a1.isEmpty() );
        }
        else
        {
            // Else we can do faster rectangular collision detection.
            Rectangle r1 = new Rectangle( item1.getXPosition(), item1.getYPosition(), item1.getWidth(), item1.getHeight() );
            Rectangle r2 = new Rectangle( item2.getXPosition(), item2.getYPosition(), item2.getWidth(), item2.getHeight() );

            if( r1.intersects( r2 ) )
            {
                return( true );
            }
            else
            {
                return( false );
            }
        }
    }

    /**
     * Returns a ByteBuffer representation of this Item
     * Used to send items over the network
     * @return ByteBuffer
     */
    @Override
    public ByteBuffer toByte()
    {
        ByteBuffer buf = super.toByte();
        buf.put(ByteChanger.pointToByte(position));
        buf.putInt(width);
        buf.putInt(height);
        buf.putFloat(angle);
        return buf;
    }

    /**
     * Returns a String representation of this Item
     * Used to save this item to a file
     * @return Save String
     */
    @Override
    public String toSaveString()
    {
        String text = super.toSaveString();
        text += position.x + "\t";
        text += position.y + "\t";
        text += width + "\t";
        text += height + "\t";
        text += angle + "\t";
        return text;
    }

    /**
     * Builds an Item from a save string, presumably read from a save
     * this item should be identical to the one saved in the file
     * @param save string
     * @param items: Master Item List used to build the action
     * @return what's left of the parem SaveString after this methods completes
     */
    @Override
    protected String fromSaveString(String text, Vector<Item> items)
    {
        text = super.fromSaveString(text, items);
        String substring = ByteChanger.getNextValue(text);
        int x = Integer.parseInt(substring);

        text = ByteChanger.incrementString(text);
        substring = ByteChanger.getNextValue(text);
        int y = Integer.parseInt(substring);
        position = new Point(x,y);

        text = ByteChanger.incrementString(text);
        substring = ByteChanger.getNextValue(text);
        width = Integer.parseInt(substring);

        text = ByteChanger.incrementString(text);
        substring = ByteChanger.getNextValue(text);
        height = Integer.parseInt(substring);

        text = ByteChanger.incrementString(text);
        substring = ByteChanger.getNextValue(text);
        angle = Float.parseFloat(substring);

        text = ByteChanger.incrementString(text);
        return text;
    }
}
